package foo.bar.blog.web.filter;

import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.TransactionStatus;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import java.io.IOException;

//import foo.bar.blog.core.services.exception.BusinessLogicException;

/**
 * @author tmjee
 * @version $Date$ $Id$
 */
public class TransactionFilter extends OncePerRequestFilter {

    private static final Log LOG = LogFactory.getLog(TransactionFilter.class);

    public static final String DEFAULT_TRANSACTION_TEMPLATE_BEAN_NAME = "transactionTemplate";

    private String transactionTemplateBeanName;

    protected void doFilterInternal(final HttpServletRequest httpServletRequest,
                                    final HttpServletResponse httpServletResponse,
                                    final FilterChain filterChain) throws ServletException, IOException {
        final TransactionTemplate transactionTemplate = lookupTransactionTemplate(httpServletRequest);
        boolean participate = false;
        if (TransactionSynchronizationManager.hasResource(transactionTemplate)) {
            LOG.info("Participating in existing transaction, TransactionSymchronizationManager's key ["+transactionTemplate+"]");
            participate = true;
        }
        else {
            LOG.info("Requesting a new transaction to be started, PlatformTransactionManager should start up a trasaction latter, TransactionSynchronizationManager's key ["+transactionTemplate.getClass()+"]");
            transactionTemplate.execute(new TransactionCallback(){
                public Object doInTransaction(TransactionStatus transactionStatus) {
                    TransactionSynchronizationManager.bindResource(transactionTemplate, transactionStatus);
                    try {
                        filterChain.doFilter(httpServletRequest, httpServletResponse);
                    }
                    catch(ServletException e) {
                        LOG.info("rolling back transaction due to ["+e+"]");
                        transactionStatus.setRollbackOnly();
                    }
                    catch(IOException e) {
                        LOG.info("rolling back transaction due to ["+e+"]");
                        transactionStatus.setRollbackOnly();
                    }
                    return null;
                }
            });
        }
        if (!participate) {
            TransactionSynchronizationManager.unbindResource(transactionTemplate);
        }
    }

    public void setTransactionTemplateBeanName(String transactionTemplateBeanName) {
        this.transactionTemplateBeanName = transactionTemplateBeanName;
    }

    protected TransactionTemplate lookupTransactionTemplate(HttpServletRequest request) {
        WebApplicationContext appContext = (WebApplicationContext) WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
        return (TransactionTemplate) appContext.getBean(transactionTemplateBeanName);
    }
}
